package com.ktr.utils;

import com.bhudy.exception.SysException;
import com.bhudy.exception.SysExceptionCode;
import com.ktr.entity.KtrAddress;
import com.ktr.entity.KtrType;
import com.ktr.service.KtrAddressService;
import com.ktr.service.KtrInfoDataService;
import com.bhudy.base.QueryFilter;
import com.ktr.entity.KtrInfo;
import com.sys.utils.Utils;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executors;

/**
 * KtrUtils 工具类
 *
 * @author com.bhudy
 * @date 2019-12-12
 */
@Component
public final class KtrUtils {

    public final static ConcurrentHashMap<String, KtrUtils> ktrUtilsConcurrentHashMap = new ConcurrentHashMap<>();
    private SerialComm serialComm;

    public static KtrUtils instance = null;

    /**
     * 将类里面的方法转成静态方法
     *
     * @return 静态KtrUtils
     */
    public static KtrUtils getInstance() {
        if (instance == null)
            instance = new KtrUtils();
        return instance;
    }

    public final CopyOnWriteArraySet<KtrInfo> ktrInfoCopyOnWriteArraySet = new CopyOnWriteArraySet<>();

    public synchronized boolean addKtrAddress(KtrInfo ktrInfo) {
        return ktrInfoCopyOnWriteArraySet.add(ktrInfo);
    }

    public synchronized boolean removeKtrAddress(KtrInfo ktrInfo) {
        return ktrInfoCopyOnWriteArraySet.remove(ktrInfo);
    }

    /**
     * 停止获取设备里面的数据
     *
     * @param ip ip地址
     * @return 是否成功
     */
    public synchronized boolean stop(String ip) {
        if (ktrUtilsConcurrentHashMap.containsKey(ip)) {
            serialComm.closePort();
            KtrUtils.ktrUtilsConcurrentHashMap.remove(ip);
            return true;
        } else {
            throw new SysException(SysExceptionCode.CODE_40002);
        }
    }

    /**
     * 运行设备
     *
     * @param ktrAddress KtrAddress
     * @return 是否成功
     */
    public synchronized boolean run(KtrAddress ktrAddress) {
        QueryFilter queryFilter = new QueryFilter();

        if (!KtrUtils.ktrUtilsConcurrentHashMap.containsKey(ktrAddress.getKtrIp())) {
            KtrUtils.ktrUtilsConcurrentHashMap.put(ktrAddress.getKtrIp(), this);
        } else {
            return true;
        }

        KtrAddressService ktrAddressService = Utils.getBean(KtrAddressService.class);
        KtrInfoDataService ktrInfoDataService = Utils.getBean(KtrInfoDataService.class);


        queryFilter.clear().and("ktrAddressId", ktrAddress.getKtrId());
        List<KtrInfo> ktrInfoList = ktrAddressService.findList(KtrInfo.class, queryFilter);
        if (Utils.isBlank(ktrInfoList)) {
            for (KtrInfo ktrInfo : ktrInfoList) {
                ktrInfoCopyOnWriteArraySet.add(ktrInfo);
            }
        }


        // 获取设备指令信息
        queryFilter.clear().and("status", 0);
        List<KtrType> ktrTypeList = ktrAddressService.findList(KtrType.class, queryFilter);
        Map<Long, KtrType> ktrTypeMap = new HashMap<>();
        if (Utils.isBlank(ktrTypeList)) {
            for (KtrType ktrType : ktrTypeList) {
                ktrTypeMap.put(ktrType.getKtrId(), ktrType);
            }
        }

        SerialComm serialComm = new SerialComm(ktrAddress.getKtrIp(), ktrAddress.getKtrPort());
        boolean isOpen = serialComm.openPort();
        this.serialComm = serialComm;

        Executors.newCachedThreadPool().execute(() -> {
            while (serialComm.isOpen()) {
                if (!Utils.isBlank(ktrInfoCopyOnWriteArraySet)) continue;

                Iterator<KtrInfo> it = ktrInfoCopyOnWriteArraySet.iterator();
                while (it.hasNext()) {
                    KtrInfo ktrInfo = it.next();
                    try {
                        byte[] resultData = serialComm.writeAndRead(ktrInfo.getKtrCode() + ktrTypeMap.get(ktrInfo.getKtrTypeId()).getKtrInstruct());
                        Double[] data = KtrUtils.analysisData(resultData, ktrInfo.getStart(), ktrInfo.getEnd(), ktrInfo.getStep());
                        if (data != null) {
                            /*List<KtrInfoData> ktrInfoDataList = ktrInfoDataService.getKtrInfoDataByCode(getAddr(resultData[0]), ktrAddress.getKtrIp());
                            if (Utils.isBlank(ktrInfoDataList)) {
                                for (KtrInfoData ktrInfoData : ktrInfoDataList) {
                                    ktrInfoDataService.updateKtrInfoData(data[ktrInfoData.getPos()], ktrInfoData.getPos(), getAddr(resultData[0]), ktrAddress.getKtrIp());
                                }
                            }*/
                        }

                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        Utils.loggerError(this, e);
                    } catch (SysException e) {
                        ktrInfo.setStatus(e.getCode());
                        ktrInfo.setRemark(e.getMessage());
                        ktrAddressService.update(ktrInfo);
                        ktrInfoCopyOnWriteArraySet.remove(ktrInfo);
                    }
                }
            }
            if (!serialComm.isOpen()) {
                ktrAddress.setKtrState(8);
                ktrAddress.setKtrMsg("通讯中断");
                ktrAddressService.update(ktrAddress);
            }
        });
        return isOpen;
    }

    /**
     * 获取设备地址码
     *
     * @param b
     * @return
     */
    public static String getAddr(byte b) {
        String str = Integer.toHexString(b).toUpperCase();
        if (str.length() != 2) {
            return "0" + str;
        } else {
            return str;
        }
    }

    /**
     * 计算CRC16校验码
     *
     * @param data 需要校验的字符串
     * @return 校验码
     */
    public static byte[] getCRC(String data) {
        data = data.replace(" ", "");
        int len = data.length();
        if (!(len % 2 == 0)) {
            return null;
        }
        int num = len / 2;
        byte[] para = new byte[num];
        for (int i = 0; i < num; i++) {
            int value = Integer.valueOf(data.substring(i * 2, 2 * (i + 1)), 16);
            para[i] = (byte) value;
        }
        return getCRC(para);
    }

    /**
     * 计算CRC16校验码
     *
     * @param bytes 字节数组
     * @return {@link String} 校验码
     * @since 1.0
     */
    public static byte[] getCRC(byte[] bytes) {
        List<Byte> byteList = new ArrayList<>();

        //CRC寄存器全为1
        int CRC = 0x0000ffff;
        //多项式校验值
        int POLYNOMIAL = 0x0000a001;
        for (int i = 0; i < bytes.length; i++) {
            byteList.add(bytes[i]);

            CRC ^= ((int) bytes[i] & 0x000000ff);
            for (int j = 0; j < 8; j++) {
                if ((CRC & 0x00000001) != 0) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        //结果转换为16进制
        String result = Integer.toHexString(CRC).toUpperCase();
        if (result.length() != 4) {
            StringBuffer sb = new StringBuffer("0000");
            result = sb.replace(4 - result.length(), 4, result).toString();
        }
        byteList.add((byte) (0XFF & Integer.valueOf(result.substring(2, 4), 16)));
        byteList.add((byte) (0XFF & Integer.valueOf(result.substring(0, 2), 16)));

        Byte[] bys = byteList.toArray(new Byte[byteList.size()]);

        byte[] bys1 = new byte[bys.length];
        for (int i = 0; i < bys.length; i++) {
            bys1[i] = bys[i];
        }

        //交换高低位
        return bys1;//result.substring(2, 4) + result.substring(0, 2);
    }


    /**
     * 计算CRC16校验码
     *
     * @param bytes 需要校验的字符串
     * @return 校验码
     */
    public static boolean checkCRC(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return false;
        }
        byte[] bs = new byte[bytes.length - 2];
        for (int i = 0; i < bytes.length - 2; i++) {
            bs[i] = bytes[i];
        }

        return bytesToHexString(bytes).equals(bytesToHexString(getCRC(bs)));
    }

    /**
     * 解析数据
     *
     * @param datas data
     * @param start 开始位置
     * @param end   结束位置
     * @param step  步长
     * @return 解析完成的数据
     */
    public static Double[] analysisData(byte[] datas, Integer start, Integer end, Integer step) {
        List<Double> resultList = new ArrayList<>();

        if (datas == null || datas.length == 0 || !checkCRC(datas)) return null;

        while (start <= end) {
            byte[] bys = new byte[step];
            for (int i = 0; i < step; i++) {
                bys[i] = datas[start + i];
            }
            Integer data = binaryToHexString(bytesToHexString(bys));

            resultList.add(Double.valueOf(data));
            start += step;
        }

        if (resultList.size() == 0) {
            return null;
        } else {
            return resultList.toArray(new Double[resultList.size()]);
        }
    }

    /**
     * 数组转换成十六进制字符串
     *
     * @param bArray
     * @return HexString
     */
    public static final String bytesToHexString(byte... bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2)
                sb.append(0);
            sb.append(sTemp.toUpperCase());
        }

        return sb.toString();
    }


    /**
     * 2进制补码方式
     * 将二进制转换为10进制输出
     * 负数补码
     *
     * @param binary 2进制
     */
    public static int binaryToHexString(String binary) {
        StringBuffer binaryString = new StringBuffer(Integer.toBinaryString(Integer.valueOf(binary, 16)));
        if (binaryString.length() == 16 && binaryString.charAt(0) == '1') {
            for (int i = 0; i < binaryString.length(); i++) {
                String code = "1";
                if (binaryString.charAt(i) == '1') {
                    code = "0";
                }
                binaryString.replace(i, i + 1, code);
            }

            //把2进制字符串key，转成10进制keys
            Integer keys = Integer.parseInt(String.valueOf(Long.valueOf(binaryString.toString()) + 1), 2);//数字2代表进制，可以是各种进制，转换成10进制
            //把10进制keys转成16进制result，toUpperCase()是把小写字母转换成大写字母
            String result = Integer.toHexString(keys).toUpperCase();

            return Integer.valueOf(result, 16);
        } else {
            return Integer.valueOf(binary, 16);
        }
    }

}
